#include "StdAfx.h"
#include "sa_TwParticleEmission.h"
#include <stdlib.h>

// define the screen resolution and keyboard macros

#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

struct CUSTOMVERTEX {FLOAT X, Y, Z; DWORD COLOR; FLOAT U, V;};
#define CUSTOMFVF (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1)
struct GRIDVERTEX {D3DXVECTOR3 position; DWORD color;};
#define GRIDFVF (D3DFVF_XYZ | D3DFVF_DIFFUSE)

LPDIRECT3DVERTEXBUFFER9 t_pbuffer = NULL;    // the pointer to the particle's vertex buffer
LPDIRECT3DVERTEXBUFFER9 g_pbuffer = NULL;    // the pointer to the grid's vertex buffer

float  random_number(float low, float high);
// texture declarations
LPDIRECT3DTEXTURE9 ptexture;



sa_TwParticleEmission::sa_TwParticleEmission(void)
{
}

sa_TwParticleEmission::~sa_TwParticleEmission(void)
{
}
void sa_TwParticleEmission::InitGraphics()
{
	// load the texture we will use
	D3DXCreateTextureFromFile(g_Render->GetDevice(),
		(LPCSTR)"fire.png",
		&ptexture);

	// create the vertices using the CUSTOMVERTEX
	struct CUSTOMVERTEX t_vert[] =
	{
		{-1.0f, 1.0f, 0.0f, D3DCOLOR_XRGB(118, 89, 55), 1, 0,},
		{-1.0f, -1.0f, 0.0f, D3DCOLOR_XRGB(118, 89, 55), 0, 0,},
		{1.0f, 1.0f, 0.0f, D3DCOLOR_XRGB(118, 89, 55), 1, 1,},
		{1.0f, -1.0f, 0.0f, D3DCOLOR_XRGB(118, 89, 55), 0, 1,},
	};

	// create a vertex buffer interface called t_buffer
	g_Render->GetDevice()->CreateVertexBuffer(4*sizeof(CUSTOMVERTEX),
		0,
		CUSTOMFVF,
		D3DPOOL_MANAGED,
		&t_pbuffer,
		NULL);

	VOID* pVoid;    // a void pointer

	// lock t_buffer and load the vertices into it
	t_pbuffer->Lock(0, 0, (void**)&pVoid, 0);
	memcpy(pVoid, t_vert, sizeof(t_vert));
	t_pbuffer->Unlock();


	InitRenderState();
}

void sa_TwParticleEmission::InitRenderState()
{
	g_Render->GetDevice()->SetRenderState(D3DRS_LIGHTING, FALSE);
	g_Render->GetDevice()->SetRenderState(D3DRS_ZENABLE, TRUE);

	g_Render->GetDevice()->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);    // turn on the color blending
	g_Render->GetDevice()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_INVDESTCOLOR);    // set source factor
	g_Render->GetDevice()->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);    // set dest factor
	g_Render->GetDevice()->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);    // set the operation


}

void sa_TwParticleEmission::InitLight()
{

}

void sa_TwParticleEmission::Update()
{

}

void sa_TwParticleEmission::Render()
{
	g_Render->GetDevice()->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    g_Render->GetDevice()->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

   g_Render->GetDevice()->BeginScene();

    // calculate the camera position
    static float index = 0.0f; index+=0.01f;
    camx = cos(index) * 15.0f; camy = 2.0f; camz = sin(index) * 15.0f;

    // set the view transform
    D3DXMATRIX matView;
    D3DXMatrixLookAtLH(&matView,
                       &D3DXVECTOR3 (camx, camy, camz),
                       &D3DXVECTOR3 (0.0f, 0.0f, 0.0f),
                       &D3DXVECTOR3 (0.0f, 1.0f, 0.0f));
    g_Render->GetDevice()->SetTransform(D3DTS_VIEW, &matView);

    // set the projection transform
    //D3DXMATRIX matProjection;
    //D3DXMatrixPerspectiveFovLH(&matProjection,
    //                           D3DXToRadian(45),
    //                           (FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT,
    //                           1.0f,
    //                           100.0f);
    //g_Render->GetDevice()->SetTransform(D3DTS_PROJECTION, &matProjection);

    // render the grid
    g_Render->GetDevice()->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
    render_grid();
	g_Render->GetDevice()->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
    run_particles();

    /* moved to the run_particles() function
    // calculate elapsed time
    static DWORD starting_point = GetTickCount(), time;
    time = GetTickCount() - starting_point;
    starting_point = GetTickCount();

    // prepare particle for rendering
    particle.run_particle(time / 1000.0f);
    particle.set_particle(camx, camy, camz);

    g_Render->GetDevice()->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
    particle.render_particle();
    */

    g_Render->GetDevice()->EndScene();

    g_Render->GetDevice()->Present(NULL, NULL, NULL, NULL);


}

void sa_TwParticleEmission::Clear()
{

}
// this is the function that positions, rotates, scales and renders the particle
void cPARTICLE::set_particle(float camx, float camy, float camz)
{
	// Before setting the world transform, do the intense mathematics!
	// a. Calculate the Differences
	static float difx, dify, difz;
	difx = camx - position.x;
	dify = camy - position.y;
	difz = camz - position.z;

	// b. Calculate the Distances
	static float FlatDist, TotalDist;
	FlatDist = sqrt(difx * difx + difz * difz);
	TotalDist = sqrt(FlatDist * FlatDist + dify * dify);

	// c. Y Rotation
	D3DXMatrixIdentity(&matRotateY);
	matRotateY._11 = matRotateY._33 = difz / FlatDist;    // cosY
	matRotateY._31 = difx / FlatDist;    // sinY
	matRotateY._13 = -matRotateY._31;    // -sinY

	// d. X Rotation
	D3DXMatrixIdentity(&matRotateX);
	matRotateX._22 = matRotateX._33 = FlatDist / TotalDist;    // cosX
	matRotateX._32 = dify / TotalDist;    // sinX
	matRotateX._23 = -matRotateX._32;    // -sinX

	// e. Tranlation
	static D3DXMATRIX matTranslate;
	D3DXMatrixTranslation(&matTranslate, position.x, position.y, position.z);

	// f. Scaling
	static D3DXMATRIX matScale;
	D3DXMatrixIdentity(&matScale);
	matScale._11 = matScale._22 = matScale._33 = radius;


	// Now build the world matrix and set it
	g_Render->GetDevice()->SetTransform(D3DTS_WORLD, &(matScale * matRotateX* matRotateY* matTranslate));

	return;
}

// this function renders the particle
void cPARTICLE::render_particle()
{
	g_Render->GetDevice()->SetFVF(CUSTOMFVF);

	g_Render->GetDevice()->SetStreamSource(0, t_pbuffer, 0, sizeof(CUSTOMVERTEX));

	g_Render->GetDevice()->SetTexture(0, ptexture);

	g_Render->GetDevice()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);

	return;
}

// this function updates the particle
void cPARTICLE::run_particle(float seconds)
{
	// handle lifespan
	life += seconds;
	if(life > lifespan)
	{
		reset_particle();
		return;
	}

	velocity += acceleration * seconds;
	position += velocity * seconds;

	return;
}

// this function sets the particle back to its original state
void cPARTICLE::reset_particle()
{
	active = false;
	position.x = 0.0f;
	position.y = 0.0f;
	position.z = 0.0f;
	velocity.x = random_number(-2.0f, 2.0f);
	velocity.y = 3.0f;
	velocity.z = random_number(-2.0f, 2.0f);
	acceleration.x = 0.0f;
	acceleration.y = random_number(15.0f, 25.0f);
	acceleration.z = 0.0f;
	radius = 1.0f;
	lifespan = 0.5f;
	life = 0.0f;

	return;
}
// this function renders the grid
void sa_TwParticleEmission::render_grid()
{
	static bool InitNeeded = true;
	static int GridSize = 10;
	static D3DXMATRIX matIdentity;

	g_Render->GetDevice()->SetFVF(GRIDFVF);

	if(InitNeeded)
	{
		g_Render->GetDevice()->CreateVertexBuffer(sizeof(GRIDVERTEX) * GridSize * 9,
			0,
			GRIDFVF,
			D3DPOOL_MANAGED,
			&g_pbuffer,
			0);

		GRIDVERTEX* pGridData = 0;
		g_pbuffer->Lock(0, 0, (void**)&pGridData, 0);

		int index = 0;
		for(; index <= GridSize * 4 + 1; index++)
		{
			float x = (index % 2) ? GridSize : -GridSize;
			float y = 0.0f;
			float z = index / 2 - GridSize;

			pGridData[index].position = D3DXVECTOR3(x, y, z);
			pGridData[index].color = D3DXCOLOR(0.4f, 0.4f, 0.4f, 1.0f);
		}

		for(; index <= GridSize * 8 + 4; index++)
		{
			static int half = index;
			float x = (index - half) / 2 - GridSize;
			float y = 0.0f;
			float z = (index % 2) ? -GridSize : GridSize;

			pGridData[index].position = D3DXVECTOR3(x, y, z);
			pGridData[index].color = D3DXCOLOR(0.4f, 0.4f, 0.4f, 1.0f);
		}

		g_pbuffer->Unlock();

		D3DXMatrixIdentity(&matIdentity);
		InitNeeded = false;
	}

	g_Render->GetDevice()->SetTransform(D3DTS_WORLD, &matIdentity);
	g_Render->GetDevice()->SetStreamSource(0, g_pbuffer, 0, sizeof(GRIDVERTEX));
	g_Render->GetDevice()->SetTexture(0, NULL);
	g_Render->GetDevice()->DrawPrimitive(D3DPT_LINELIST, 0, GridSize * 4 + 2);

	return;
}

// this function generates random float values
float  random_number(float low, float high)
{
	return low + ((float)((float)rand() / (float)RAND_MAX) * (float)((high) - (low)));
}
// this function goes through and runs each active particle
void sa_TwParticleEmission::run_particles()
{
	// calculate elapsed time
	static DWORD starting_point = GetTickCount(), time;
	time = GetTickCount() - starting_point;
	starting_point = GetTickCount();

	// turn on alpha blending
	g_Render->GetDevice()->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);

	// run and render all active particles
	g_Render->GetDevice()->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
	for(int index = 0; index < PARTICLECOUNT; index++)
	{
		if(particle[index].active)
		{
			particle[index].run_particle(time / 1000.0f);
			particle[index].set_particle(camx, camy, camz);
			particle[index].render_particle();
		}
	}
	g_Render->GetDevice()->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);

	for(int index = 0; index < time / 10.0f; index++)
	{
		for(int index = 0; index < PARTICLECOUNT; index++)
		{
			if(!particle[index].active)    // if the particle is inactive...
			{
				particle[index].active = true;    // ...activate it
				break;    // then stop
			}
		}
	}

	return;
}

